package site.interceptor;

import com.google.common.base.Strings;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import site.exception.MessageException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ErrorHandlingInterceptor extends AbstractInterceptor {
    private static final Logger logger = Logger.getLogger(ErrorHandlingInterceptor.class);

    private static final short AJAX_ERROR_HTTP_CODE = 601;
    private static final String DEFAULT_MSG = "系统繁忙，请稍候再试。";

    private static final String RESULT_KEY = "RESULT";
    private static final String ERROR_KEY = "errorMsg";
    private static final String FRONT_INPUT = "FrontInput";

    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        try {
            return invocation.invoke();
        } catch (Exception e) {
            if (e instanceof MessageException) {
                logger.error(e.getMessage());
            } else {
                logger.error(e);
            }

            if (isAjaxRequest()) {
                handleAjaxError(e);
                return Action.NONE;
            }

            handleFormError(e);

            return (String) ServletActionContext.getRequest().getAttribute(RESULT_KEY);
        }
    }

    private void handleAjaxError(Exception e) {
        HttpServletResponse response = ServletActionContext.getResponse();
        String rspMsg = DEFAULT_MSG;
        try {
            boolean handled = false;
            Throwable throwEx = e;

            while (throwEx != null) {
                if (throwEx instanceof MessageException) {
                    rspMsg = throwEx.getMessage();
                    handled = true;
                }
                throwEx = throwEx.getCause();
            }
            if (!handled) {
                logExceptionMsg(e);
            }

            response.sendError(AJAX_ERROR_HTTP_CODE, buildErrorMsg(rspMsg));
        } catch (Exception ee) {
            logExceptionMsg(ee);
            try {
                response.sendError(AJAX_ERROR_HTTP_CODE, buildErrorMsg(rspMsg));
            } catch (IOException io) {
            }
        }
    }

    private void handleFormError(Exception e) {
        String rspMsg = DEFAULT_MSG;
        try {
            boolean handled = false;
            Throwable throwEx = e;

            while (throwEx != null) {
                if (throwEx instanceof MessageException) {
                    rspMsg = throwEx.getMessage();
                    handled = true;
                }
                throwEx = throwEx.getCause();
            }
            if (!handled) {
                logExceptionMsg(e);
            }

            fillError4Display(FRONT_INPUT, rspMsg);
        } catch (Exception ee) {
            logExceptionMsg(ee);
            fillError4Display(FRONT_INPUT, rspMsg);
        }
    }

    private boolean isAjaxRequest() {
        HttpServletRequest request = ServletActionContext.getRequest();
        String header = request.getHeader("X-Requested-With");
        if (header != null && "XMLHttpRequest".equals(header)) {
            return true;
        }
        return false;
    }

    private void fillError4Display(String result, String msg) {
        ServletActionContext.getRequest().setAttribute(RESULT_KEY, result);
        ServletActionContext.getRequest().setAttribute(ERROR_KEY, buildErrorMsg(msg));
    }

    private String buildErrorMsg(String msg) {
        return msg + Strings.repeat(" ", 1000);
    }

    private void logExceptionMsg(Throwable t) {
        Throwable cause = getRootCause(t);
        logger.error(cause.getMessage(), cause);
    }

    private Throwable getRootCause(Throwable t) {
        Throwable cause = t;
        while (cause.getCause() != null) {
            cause = cause.getCause();
        }
        return cause;
    }
}
